# 装饰器的使用背景
# 1. 我们需要抽离出一段和原函数主逻辑不相关的逻辑，一般来说就是打日志
# 2. 通过自定义一个装饰器的形式，实现批量函数的指定逻辑的增强

import time


# 函数式装饰器
def run_time(func):  # 定义一个函数的装饰器，装饰器的入参指向的是被装饰器修饰的函数 -> 一切皆变量/对象，变量可以作为入参
    def wrapper(*args, **kwargs):  # 代理函数的定义和可变参数 -> 什么叫代理、什么叫代理函数、以及可变参数的回顾
        start_time = time.time()  # 被代理函数的前置扩展点
        result = func(*args, **kwargs)  # 代理函数调用被代理函数
        end_time = time.time()  # 被代理函数的后置扩展点
        print(f"耗时:{end_time - start_time}")
        return result  # 处理被代理函数的返回结果，如果不返回，装饰器就不会返回值

    return wrapper  # 一切皆变量，变量可以作为返回值

def run_time_with_param(param):  # 当我们需要给装饰器指定传入参数的时候，需要额外多嵌套一层函数
    def out_wrapper(func):
        def wrapper(*args):  # *args 带有的时候允许被装饰的函数传入参数
            print(param)
            start_time = time.time()
            result = func(*args)  # result 带有的时候，允许返回结果，同时需要再 wrapper内部return
            end_time = time.time()
            print(f"耗时:{end_time - start_time}")
            return result

        return wrapper

    return out_wrapper


class RunTime:  # 定义一个类装饰器（是指通过类来实现装饰器，并不是说这个装饰器可以装饰类，目前的情况它还是用于装饰函数）
    def __init__(self, func):  # 传入被代理函数
        self.func = func  # 将被代理函数作为装饰器实例的属性

    def __call__(self, *args, **kwargs):
        start_time = time.time()  # 被代理函数的前置扩展点
        result = self.func(*args, **kwargs)  # 代理函数调用被代理函数
        end_time = time.time()  # 被代理函数的后置扩展点
        print(f"耗时:{end_time - start_time}")
        return result  # 处理被代理函数的返回结果，如果不返回，装饰器就不会返回值


def is_prime(num):
    """
    是否是一个质数（它的因数，只有它自己和 1），2，3，4
    :param num:
    :return:
    """
    if not isinstance(num, int):
        return False
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True


# @run_time
# def get_prime_nums():
#     """
#     打印所有的质数
#     :return:
#     """
#     for i in range(1, 10001):
#         if is_prime(i):
#             print(i)


# @run_time_with_param("白羽专用装饰器")
@run_time
def count_prime_nums(num):
    total_count = 0
    for i in range(1, num + 1):
        if is_prime(i):
            total_count += 1
    return total_count


if __name__ == "__main__":  # python 的执行入口是否为当前文件
    # get_prime_nums()
    count_num = count_prime_nums(num=10000)
    # 当我们调用 count_prime_nums 函数时，我们可以分为两步
    # 因为函数也是变量的一种，因此调用函数的步骤也就是 1. 找到函数 2. 调用它（加括号，把参数传进去）
    # 调用被装饰的函数时，本质是对装饰器的调用
    # 找到 count_prime_nums 本身 等价于 调用 run_time 等价于 找到 wrapper 本身；
    # 调用 count_prime_nums 等价于 调用 wrapper


    print(count_num)

